package com.dongzili.binlog.common.config.es;

import org.dromara.easyes.annotation.EsDS;
import org.dromara.easyes.annotation.Intercepts;
import org.dromara.easyes.common.constants.BaseEsConstants;
import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
import org.dromara.easyes.common.utils.LogUtils;
import org.dromara.easyes.common.utils.RestHighLevelClientUtils;
import org.dromara.easyes.common.utils.TypeUtils;
import org.dromara.easyes.core.biz.EntityInfo;
import org.dromara.easyes.core.cache.BaseCache;
import org.dromara.easyes.core.cache.GlobalConfigCache;
import org.dromara.easyes.core.config.GlobalConfig;
import org.dromara.easyes.core.proxy.EsMapperProxy;
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
import org.dromara.easyes.extension.context.Interceptor;
import org.dromara.easyes.extension.context.InterceptorChain;
import org.dromara.easyes.extension.context.InterceptorChainHolder;
import org.dromara.easyes.starter.config.EasyEsConfigProperties;
import org.dromara.easyes.starter.factory.IndexStrategyFactory;
import org.dromara.easyes.starter.service.AutoProcessIndexService;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class EsMapperFactoryBean<T> {

    @Autowired
    private RestHighLevelClientUtils restHighLevelClientUtils;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private IndexStrategyFactory indexStrategyFactory;
    @Autowired
    private EasyEsConfigProperties esConfigProperties;

//    public EsMapperFactoryBean(Class<T> mapperInterface) {
//        this.mapperInterface = mapperInterface;
//    }

    public T getObject(Class<?> mapperInterface) {
        EsMapperProxy<?> esMapperProxy = new EsMapperProxy<>(mapperInterface, new ConcurrentHashMap<>());
        Class<?> entityClass = TypeUtils.getInterfaceT(mapperInterface, BaseEsConstants.ZERO);
        GlobalConfigCache.setGlobalConfig(this.esConfigProperties.getGlobalConfig());
        String restHighLevelClientId = Optional.ofNullable(mapperInterface.getAnnotation(EsDS.class)).map(EsDS::value).orElse("DEFAULT_DS");
        RestHighLevelClient client = this.restHighLevelClientUtils.getClient(restHighLevelClientId);
        BaseCache.initCache(mapperInterface, entityClass, client);
        T t = (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy);
        InterceptorChain interceptorChain = this.initInterceptorChain();
        GlobalConfig globalConfig = this.esConfigProperties.getGlobalConfig();
        if (!ProcessIndexStrategyEnum.MANUAL.equals(globalConfig.getProcessIndexMode())) {
            EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass);
            boolean isChild = entityInfo.isChild();
            if (!isChild) {
                AutoProcessIndexService autoProcessIndexService = this.indexStrategyFactory.getByStrategyType(globalConfig.getProcessIndexMode().getStrategyType());
                autoProcessIndexService.processIndexAsync(entityClass, client);
            }
        } else {
            LogUtils.info("===> manual index mode activated");
        }

        return interceptorChain.pluginAll(t);
    }

    private InterceptorChain initInterceptorChain() {
        InterceptorChainHolder interceptorChainHolder = InterceptorChainHolder.getInstance();
        InterceptorChain interceptorChain = interceptorChainHolder.getInterceptorChain();
        if (interceptorChain == null) {
            synchronized(this) {
                interceptorChainHolder.initInterceptorChain();
                Map<String, Object> beansWithAnnotation = this.applicationContext.getBeansWithAnnotation(Intercepts.class);
                beansWithAnnotation.forEach((key, val) -> {
                    if (val instanceof Interceptor) {
                        Interceptor interceptor = (Interceptor)val;
                        interceptorChainHolder.addInterceptor(interceptor);
                    }

                });
            }
        }

        return interceptorChainHolder.getInterceptorChain();
    }
}
